#include "EdoModulator.h"
#include "EdoModulatorManager.h"

EdoModulator::EdoModulator(const uint8 _controllerMidiType) 
			:	currentValue(0), 
				assosiatedComponent(0), 
				controllerMidiType(_controllerMidiType),
				modulatorBytePosition(-1),
				channelBytePosition(-1),
				rawData(0),
				rawDataSize(0),
				usesExternalData(false),
				transmitOnValueChange(true)
{
}

EdoModulator::~EdoModulator()
{
}

void EdoModulator::fromString(const String &stringVersion)
{
	formulaString = stringVersion;

	if (stringVersion.startsWith (T("$")))
	{
		EdoModulator *m = EdoModulatorManager::getInstance()->getStaticValue (stringVersion.fromFirstOccurrenceOf (T("$"), false, false));
		if (m)
		{
			usesExternalData = true;

			rawData		= m->getData();
			rawDataSize	= m->getDataSize();
		}
	}
	else
	{
		StringArray toks;
		toks.addTokens (stringVersion.trim(), T(",; \t\r\n"), 0);	
		if (toks.size() == 0)
		{
			return;
		}
		
		dataStorage = MemoryBlock(toks.size(), true);

		for (int i=0; i<toks.size(); i++)
		{
			uint8 val	= toks[i].getHexValue32();
			dataStorage[i] = val;
	
			if (val == 0) /* might be a modulator */
			{
				if (toks[i] == T("xx"))
				{
					modulatorBytePosition = i;
				}	
				if (toks[i] == T("yy"))
				{
					channelBytePosition = i;
				}
			}
		}

		usesExternalData = false;
	}

	Log (T("EdoController::fromString ") + String::toHexString ((uint8 *)rawData, rawDataSize));
}

void EdoModulator::setValue (const int64 &newValue)
{
	expectedNewValue = newValue;
	valueChanged();
}

void EdoModulator::setAssosiatedComponent (Component *componentToAssosiate)
{
	assosiatedComponent = componentToAssosiate;
}

void EdoModulator::valueChanged()
{
	switch (controllerMidiType)
	{
		case EdoModulatorManager::MidiCC:
		case EdoModulatorManager::MidiNoteOn:
		case EdoModulatorManager::MidiNoteOff:
		case EdoModulatorManager::MidiRPN:
		case EdoModulatorManager::MidiNRPN:
			break;
		case EdoModulatorManager::MidiSysEx:
			sysEx();
			break;
		case EdoModulatorManager::MidiProgramChange:
		case EdoModulatorManager::MidiBankChange:
		case EdoModulatorManager::MidiActiveSense:
		case EdoModulatorManager::MidiAfterTouch:
		case EdoModulatorManager::MidiChannelPressure:
		case EdoModulatorManager::MidiClockStart:
		case EdoModulatorManager::MidiClockStop:
		case EdoModulatorManager::MidiClockFrame:
		case EdoModulatorManager::MidiClockContinue:
			break;
	}
}

void EdoModulator::sysEx()
{
	if (expectedNewValue == currentValue && transmitOnValueChange == false)
	{
		/* the new value is the same as the current value, don't change eanything, don't send no midi messages */
		return;
	}

	if (usesExternalData)
	{
		if (rawDataSize > 0 && channelBytePosition < rawDataSize && modulatorBytePosition < rawDataSize)
		{
			uint8 *ptr						= (uint8 *)rawData;
			*(ptr+channelBytePosition)		= (uint8)getMidiChannel();
			*(ptr+modulatorBytePosition)	= (uint8)currentValue;
		}

		Log (T("EdoController::sysEx ") + String::toHexString ((uint8 *)rawData, rawDataSize));

		EdoModulatorManager::getInstance()->addMessageToQueue ((uint8 *)rawData, rawDataSize);
	}
	else
	{
		dataStorage[modulatorBytePosition]	= (uint8)currentValue;
		dataStorage[channelBytePosition]	= getMidiChannel();

		Log (T("EdoController::sysEx ") + String::toHexString (dataStorage, dataStorage.getSize()));

		EdoModulatorManager::getInstance()->addMessageToQueue (dataStorage, dataStorage.getSize());
	}

	currentValue = expectedNewValue;
}

const uint8 EdoModulator::getMidiChannel()
{
	return (EdoModulatorManager::getInstance()->getMidiChannel());
}

void *EdoModulator::getData()
{
	if (usesExternalData)
	{
		return (rawData);
	}
	else
	{
		return (dataStorage.getData());
	}
}

const int EdoModulator::getDataSize()
{
	if (usesExternalData)
	{
		return (rawDataSize);
	}
	else
	{
		return (dataStorage.getSize());
	}
}

const String &EdoModulator::getName()
{
	return (controllerName);
}